package com.mdb.open.trade.facade;

import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.mdb.bc.api.data.ListResultEx;
import com.mdb.bc.api.data.ResultEx;
import com.mdb.bc.api.enums.ApiEnum;
import com.mdb.bc.cache.sequence.util.SequenceGenerator;
import com.mdb.bc.database.mybatisplus.PageUtil;
import com.mdb.bc.utils.ObjectUtil;
import com.mdb.enums.CcyTypeEnum;
import com.mdb.enums.CustomerTypeEnum;
import com.mdb.open.common.enums.TradeOpenRetCodeEnum;
import com.mdb.open.common.enums.product.status.LimitUseRecStatusEnum;
import com.mdb.open.common.enums.trade.type.MerchantTypeEnum;
import com.mdb.open.trade.common.MdbDataSource;
import com.mdb.open.trade.config.LimitNoSeqEnum;
import com.mdb.open.trade.config.LimitUseSeqEnum;
import com.mdb.open.trade.config.MdbProdProperties;
import com.mdb.open.trade.domain.dto.product.*;
import com.mdb.open.trade.domain.ko.product.CustomerLimitKo;
import com.mdb.open.trade.domain.ko.product.LimitUserProdKo;
import com.mdb.open.trade.domain.ko.product.MerchantLimitKo;
import com.mdb.open.trade.domain.ko.product.QueryMerchantProdLimitPageKo;
import com.mdb.open.trade.domain.po.product.*;
import com.mdb.open.trade.domain.po.trade.MerchantInfo;
import com.mdb.open.trade.domain.vo.product.*;
import com.mdb.open.trade.event.model.LimitInfoHisEvent;
import com.mdb.open.trade.event.model.LimitUserProdHisEvent;
import com.mdb.open.trade.feign.OutreachClient;
import com.mdb.open.trade.feign.dto.CoreLimitBodyDto;
import com.mdb.open.trade.feign.dto.CoreLimitDto;
import com.mdb.open.trade.feign.vo.CoreLimitVo;
import com.mdb.open.trade.mapper.product.LimitExMapper;
import com.mdb.open.trade.service.product.*;
import com.mdb.open.trade.service.trade.TfMerchantInfoService;
import com.mdb.user.LoginUserInfoHelper;
import com.mdb.user.feign.vo.LoginInfoVo;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

/**
 * @author liuyi
 * @date 2019/07/16
 **/
@Log4j2
@Service
@DS(MdbDataSource.PRODUCT)
public class LimitFacade {

    @Autowired
    private LimitUserProdService limitUserProdService;

    @Autowired
    private LimitInfoService limitInfoService;

    @Autowired
    private LimitChangeRecService limitChangeRecService;

    @Autowired
    private LimitUseRecService limitUseRecService;

    @Autowired
    private LimitRecoverRecService limitRecoverRecService;

    @Autowired
    private LimitExMapper limitExMapper;

    @Autowired
    private LoginUserInfoHelper loginUserInfoHelper;

    @Autowired
    private SequenceGenerator sequenceGenerator;

    @Autowired
    private MdbProdProperties mdbProdProperties;

    @Autowired
    private OutreachClient outreachClient;

    @Autowired
    private ApplicationEventPublisher publisher;

    @Autowired
    private TfMerchantInfoService tfMerchantInfoService;

    public static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd");

    /**
     * 修改商户额度
     */
    public ResultEx<MerchantLimitVo> updateMerchantLimit(UpdateMerchantLimitDto dto) {
        //查询商户额度
        LimitUserProd existLimitUserProd = limitUserProdService.getMerchantLimit(dto.getCustomerCode(), dto.getProdCode(), dto.getMerchantNo());

        //商户额度未设置
        if (ObjectUtil.isEmpty(existLimitUserProd)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.MERCHANT_LIMIT_EMPTY_ERROR);
        }

        return updateMerchantLimit(existLimitUserProd, dto);
    }

    /**
     * 创建商户额度
     */
    @Transactional
    public ResultEx<MerchantLimitVo> saveMerchantLimit(SaveMerchantLimitDto dto) {
        //查询商户额度是否已存在，如果存在则修改
        LimitUserProd existLimitUserProd = limitUserProdService.getMerchantLimit(dto.getCustomerCode(), dto.getProdCode(), dto.getMerchantNo());

        String limitCcy = dto.getLimitCcy();
        if (ObjectUtil.isEmpty(limitCcy)) {
            limitCcy = CcyTypeEnum.USD.getValue();
        }
        //新增
        if (ObjectUtil.isEmpty(existLimitUserProd)) {
            //新增额度
            CreateMerchantLimitDto createMerchantLimitDto = new CreateMerchantLimitDto();
            createMerchantLimitDto.setProdCode(dto.getProdCode());
            createMerchantLimitDto.setCustomerCode(dto.getCustomerCode());
            createMerchantLimitDto.setMerchantNo(dto.getMerchantNo());
            createMerchantLimitDto.setMerchantType(dto.getMerchantType());
            createMerchantLimitDto.setMerchantName(dto.getMerchantName());
            createMerchantLimitDto.setTotalBal(dto.getTotalBal());
            createMerchantLimitDto.setLimitCcy(limitCcy);
            createMerchantLimitDto.setRemark(dto.getRemark());
            createMerchantLimitDto.setExpDate(dto.getExpDate());
            return createMerchantLimit(createMerchantLimitDto);
        }

        UpdateMerchantLimitDto updateMerchantLimitDto = new UpdateMerchantLimitDto();
        updateMerchantLimitDto.setProdCode(dto.getProdCode());
        updateMerchantLimitDto.setCustomerCode(dto.getCustomerCode());
        updateMerchantLimitDto.setMerchantNo(dto.getMerchantNo());
        updateMerchantLimitDto.setMerchantName(dto.getMerchantName());
        updateMerchantLimitDto.setTotalBal(dto.getTotalBal());
        updateMerchantLimitDto.setLimitCcy(limitCcy);
        updateMerchantLimitDto.setRemark(dto.getRemark());
        updateMerchantLimitDto.setExpDate(dto.getExpDate());

        return updateMerchantLimit(existLimitUserProd, updateMerchantLimitDto);
    }

    /**
     * 额度调整
     */
    @Transactional
    public ResultEx<MerchantLimitVo> updateMerchantLimit(LimitUserProd existLimitUserProd, UpdateMerchantLimitDto dto) {
        //调整额度
        LimitInfo existLimitInfo = limitInfoService.getByLimitNo(existLimitUserProd.getLimitNo());

        //修改limit_info中额度有效期重置 平台下次重检日期 当前时间增加 month个月
        int earlyDays = mdbProdProperties.getLimitExtendEarlyDays();

        LocalDateTime now = LocalDateTime.now();
        LimitInfo updateLimitInfo = new LimitInfo();
        updateLimitInfo.setId(existLimitInfo.getId());
        updateLimitInfo.setCcy(dto.getLimitCcy());
        updateLimitInfo.setEffDate(now);
        if (ObjectUtil.isNotEmpty(dto.getExpDate())) {
            updateLimitInfo.setExpDate(dto.getExpDate());
            updateLimitInfo.setPlatformNrvwDate(dto.getExpDate().plusDays(-earlyDays));
        }
        updateLimitInfo.setPlatformLrvwDate(now);
        updateLimitInfo.setPlatformUpdateDate(now);
        updateLimitInfo.setRemark(dto.getRemark());
        updateLimitInfo.setActual(dto.getTotalBal());
        updateLimitInfo.setMax(NumberUtil.max(dto.getTotalBal(), existLimitInfo.getMax()));

        //总额度变化值
        BigDecimal availBal = dto.getTotalBal().subtract(existLimitInfo.getUseBal()).subtract(existLimitInfo.getEmkBal());
        if (availBal.compareTo(BigDecimal.ZERO) < 0) {
            availBal = BigDecimal.ZERO;
        }

        updateLimitInfo.setAvailBal(availBal);

        limitInfoService.updateById(updateLimitInfo);

        //增加历史记录
        LimitInfoHisEvent event = new LimitInfoHisEvent();
        event.setLimitInfo(existLimitInfo);
        publisher.publishEvent(event);

        //新增额度变化记录
        LimitChangeRec limitChangeRec = new LimitChangeRec();
        limitChangeRec.setLimitNo(existLimitInfo.getLimitNo());
        limitChangeRec.setChangeTime(updateLimitInfo.getPlatformUpdateDate());
        limitChangeRec.setLastTotalBal(existLimitInfo.getActual());
        limitChangeRec.setNewTotalBal(dto.getTotalBal());
        limitChangeRec.setRemark(dto.getRemark());
        //操作人员
        LoginInfoVo loginInfoVo = loginUserInfoHelper.getLoginInfo();
        if (ObjectUtil.isNotEmpty(loginInfoVo)) {
            limitChangeRec.setOptUserId(loginInfoVo.getUserId());
            limitChangeRec.setOptUserName(loginInfoVo.getUserName());
        }
        limitChangeRecService.save(limitChangeRec);

        if (!StrUtil.equals(dto.getMerchantName(), existLimitUserProd.getUserName())) {
            //修改商户额度产品表
            LimitUserProdHisEvent limitInfoHisEvent = new LimitUserProdHisEvent();
            limitInfoHisEvent.setLimitUserProd(existLimitUserProd);
            limitInfoHisEvent.setUpdateMerchantLimitDto(dto);
            publisher.publishEvent(limitInfoHisEvent);
        }

        MerchantLimitVo merchantLimitVo = new MerchantLimitVo();
        merchantLimitVo.setLimitNo(existLimitUserProd.getLimitNo());
        merchantLimitVo.setProdCode(dto.getProdCode());
        merchantLimitVo.setCustomerCode(dto.getCustomerCode());
        merchantLimitVo.setMerchantNo(dto.getMerchantNo());
        merchantLimitVo.setMerchantType(ApiEnum.parse(MerchantTypeEnum.class, existLimitUserProd.getUserType()));
        merchantLimitVo.setMerchantName(existLimitUserProd.getUserName());
        merchantLimitVo.setTotalBal(dto.getTotalBal());
        merchantLimitVo.setAvailBal(availBal);
        merchantLimitVo.setUsedBal(existLimitInfo.getUseBal());
        merchantLimitVo.setLimitCcy(dto.getLimitCcy());
        return ResultEx.ok(merchantLimitVo);
    }

    /**
     * 新增商户额度
     */
    @Transactional
    public ResultEx<MerchantLimitVo> createMerchantLimit(CreateMerchantLimitDto dto) {
        //查询是否有客户产品额度存在,不存在则不允许创建，客户产品状态是否校验..?
        CustomerLimitKo existCustomerLimitProd = limitExMapper.getCustomerLimitDetail(dto.getCustomerCode(), dto.getProdCode());
        if (ObjectUtil.isEmpty(existCustomerLimitProd)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.CUST_PROD_LIMIT_EMPTY_ERROR);
        }

        //新增limit_info记录
        //生成额度编号
        String limitNo = sequenceGenerator.generator(LimitNoSeqEnum.ME_LIMIT_NO);
        int earlyDays = mdbProdProperties.getLimitExtendEarlyDays();

        LocalDateTime now = LocalDateTime.now();
        LimitInfo limitInfo = new LimitInfo();
        limitInfo.setType("1");
        limitInfo.setLimitNo(limitNo);
        limitInfo.setCcy(dto.getLimitCcy());
        limitInfo.setMax(dto.getTotalBal());
        limitInfo.setActual(dto.getTotalBal());
        limitInfo.setAvailBal(dto.getTotalBal());
        limitInfo.setEmkBal(new BigDecimal(0));
        limitInfo.setUseBal(new BigDecimal(0));
        limitInfo.setEffDate(now);
        limitInfo.setExpDate(dto.getExpDate());
        limitInfo.setAvailDate(now);
        limitInfo.setPlatformLrvwDate(now);
        limitInfo.setPlatformUpdateDate(now);
        limitInfo.setPlatformNrvwDate(dto.getExpDate().plusDays(-earlyDays));
        limitInfo.setLimitType(CustomerTypeEnum.MERCHANT.getValue());

        //新增limit_user_prod记录
        LimitUserProd limitUserProd = new LimitUserProd();
        limitUserProd.setType(CustomerTypeEnum.MERCHANT.getValue());
        limitUserProd.setPUserNo(dto.getCustomerCode());
        limitUserProd.setUserNo(dto.getMerchantNo());
        limitUserProd.setUserName(dto.getMerchantName());
        limitUserProd.setUserType(dto.getMerchantType().getValue());
        limitUserProd.setProdCode(dto.getProdCode());
        limitUserProd.setLimitNo(limitInfo.getLimitNo());

        //新增额度变化记录
        LimitChangeRec limitChangeRec = new LimitChangeRec();
        limitChangeRec.setLimitNo(limitInfo.getLimitNo());
        limitChangeRec.setChangeTime(limitInfo.getPlatformUpdateDate());
        limitChangeRec.setLastTotalBal(new BigDecimal(0));
        limitChangeRec.setNewTotalBal(dto.getTotalBal());
        limitChangeRec.setRemark("新建");

        limitInfoService.save(limitInfo);
        limitUserProdService.save(limitUserProd);
        limitChangeRecService.save(limitChangeRec);

        MerchantLimitVo vo = new MerchantLimitVo();
        vo.setLimitNo(limitUserProd.getLimitNo());
        vo.setProdCode(limitUserProd.getProdCode());
        vo.setCustomerCode(limitUserProd.getPUserNo());
        vo.setMerchantNo(limitUserProd.getUserNo());
        vo.setMerchantType(ApiEnum.parse(MerchantTypeEnum.class, limitUserProd.getUserType()));
        vo.setMerchantName(limitUserProd.getUserName());
        vo.setTotalBal(limitInfo.getActual());
        vo.setAvailBal(limitInfo.getAvailBal());
        vo.setUsedBal(new BigDecimal(0));
        vo.setLimitCcy(limitInfo.getCcy());

        return ResultEx.ok(vo);
    }


    /**
     * 获取客户下商户额度列表
     */
    public ListResultEx<LimitUserProdVo> queryMerchantProdLimitPageList(QueryMerchantProdLimitPageDto dto) {
        //商户产品额度查询
        QueryMerchantProdLimitPageKo queryMerchantProdLimitPageKo = new QueryMerchantProdLimitPageKo();
        queryMerchantProdLimitPageKo.setProdCode(dto.getProdCode());
        queryMerchantProdLimitPageKo.setCustomerCode(dto.getCustomerCode());
        queryMerchantProdLimitPageKo.setMerchantNo(dto.getMerchantNo());
        queryMerchantProdLimitPageKo.setMerchantName(dto.getMerchantName());
        queryMerchantProdLimitPageKo.setMerchantType(ObjectUtil.isEmpty(dto.getMerchantType()) ? null : dto.getMerchantType().getValue());
        queryMerchantProdLimitPageKo.setPageNo(dto.getPageNo());
        queryMerchantProdLimitPageKo.setPageSize(dto.getPageSize());
        queryMerchantProdLimitPageKo.setSort(dto.getSort());
        queryMerchantProdLimitPageKo.setOrder(dto.getOrder());
        IPage<LimitUserProdKo> page = limitExMapper.queryMerchantProdLimitPageList(PageUtil.initPage(dto), queryMerchantProdLimitPageKo);

        IPage<LimitUserProdVo> returnPage = page.convert(limitUserProd -> {
            LimitUserProdVo limitUserProdVo = new LimitUserProdVo();
            limitUserProdVo.setId(limitUserProd.getId());
            limitUserProdVo.setType(limitUserProd.getType());
            limitUserProdVo.setPUserNo(limitUserProd.getPUserNo());
            limitUserProdVo.setUserNo(limitUserProd.getUserNo());

            //获取商户名称
            ResultEx<MerchantInfo> merchantInfoEx = tfMerchantInfoService.getMerchantInfo(limitUserProd.getPUserNo(), limitUserProd.getUserNo());
            if (merchantInfoEx.isSuccess() && ObjectUtil.isNotEmpty(merchantInfoEx.getData())) {
                limitUserProdVo.setUserName(merchantInfoEx.getData().getMerchantNameCn());
            } else {
                limitUserProdVo.setUserName(limitUserProd.getUserName());
            }
            limitUserProdVo.setUserType(limitUserProd.getUserType());
            limitUserProdVo.setProdCode(limitUserProd.getProdCode());
            limitUserProdVo.setLimitNo(limitUserProd.getLimitNo());
            limitUserProdVo.setLimitCcy(limitUserProd.getLimitCcy());
            limitUserProdVo.setTotalBal(limitUserProd.getTotalBal());
            limitUserProdVo.setAvailBal(limitUserProd.getAvailBal());
            limitUserProdVo.setUsedBal(limitUserProd.getUsedBal());
            limitUserProdVo.setFreezedBal(limitUserProd.getFreezedBal());
            limitUserProdVo.setEffDate(limitUserProd.getEffDate());
            limitUserProdVo.setExpDate(limitUserProd.getExpDate());
            limitUserProdVo.setCreateUserId(limitUserProd.getCreateUserId());
            limitUserProdVo.setCreateUserName(limitUserProd.getCreateUserName());
            limitUserProdVo.setOptUserId(limitUserProd.getOptUserId());
            limitUserProdVo.setOptUserName(limitUserProd.getOptUserName());
            limitUserProdVo.setCreateTime(limitUserProd.getCreateTime());
            limitUserProdVo.setUpdateTime(limitUserProd.getUpdateTime());
            return limitUserProdVo;
        });

        ListResultEx<LimitUserProdVo> listResultEx = ListResultEx.ok(returnPage.getRecords());
        listResultEx.setPageNo(page.getCurrent());
        listResultEx.setPageSize(page.getSize());
        listResultEx.setTotal(page.getTotal());
        return listResultEx;
    }



    /**
     * 获取客户额度
     */
    public ResultEx<CustomerLimitVo> getCustProdLimit(String customerCode, String prodCode) {
        LimitUserProd customerLimit = limitUserProdService.getCustomerLimit(customerCode, prodCode);
        if (ObjectUtil.isEmpty(customerLimit)) {
            return ResultEx.fail();
        }
        CustomerLimitVo customerLimitVo = new CustomerLimitVo();
        customerLimitVo.setLimitNo(customerLimit.getLimitNo());
        customerLimitVo.setProdCode(customerLimit.getProdCode());
        customerLimitVo.setCustomerCode(customerLimit.getUserNo());
        customerLimitVo.setCustomerName(customerLimit.getUserName());

        LimitInfoVo coreLimitInfoVo = getLimitInfoFromCore(customerLimit.getLimitNo());
        if (ObjectUtil.isEmpty(coreLimitInfoVo)) {
            customerLimitVo.setTotalBal(BigDecimal.ZERO);
            customerLimitVo.setAvailBal(BigDecimal.ZERO);
            customerLimitVo.setUsedBal(BigDecimal.ZERO);
            log.error("查询核心额度结果为空。limitNo:{}", customerLimit.getLimitNo());
            return ResultEx.ok(customerLimitVo);
        }

        customerLimitVo.setTotalBal(coreLimitInfoVo.getActual());
        customerLimitVo.setAvailBal(coreLimitInfoVo.getAvaBal());
        customerLimitVo.setUsedBal(coreLimitInfoVo.getUseBal());
        customerLimitVo.setLimitCcy(coreLimitInfoVo.getCcy());
        customerLimitVo.setEffDate(coreLimitInfoVo.getEffDate());
        customerLimitVo.setExpDate(coreLimitInfoVo.getExpDate());
        customerLimitVo.setUpdateTime(coreLimitInfoVo.getUpdateTime());
        return ResultEx.ok(customerLimitVo);
    }


    /**
     * 根据额度编号查询核心额度详情
     */
    private LimitInfoVo getLimitInfoFromCore(String limitNo) {
//        limitNo = "1202";//查询核心额度先都查询这个额度，对接完成后放开
        CoreLimitDto coreLimitDto = new CoreLimitDto();
        CoreLimitBodyDto coreLimitBodyDto = new CoreLimitBodyDto();
        coreLimitBodyDto.setLmtNo(limitNo);
        coreLimitDto.setBody(coreLimitBodyDto);
        ResultEx<CoreLimitVo> coreLimitVoResultEx = outreachClient.requestCoreLimitApi0351070(coreLimitDto);
        CoreLimitVo coreLimitVo = coreLimitVoResultEx.getData();
        if (ObjectUtil.isEmpty(coreLimitVo)) {
            log.error("核心额度信息查询结果为空，limitNo:{}", limitNo);
            return null;
        }
        LimitInfo byLimitNo = limitInfoService.getByLimitNo(limitNo);

        LimitInfoVo limitInfoVo = new LimitInfoVo();
        limitInfoVo.setType(coreLimitVo.getRType());
        limitInfoVo.setLimitNo(coreLimitVo.getLmtNo());
        limitInfoVo.setCcy(coreLimitVo.getCcy());
        limitInfoVo.setCode(coreLimitVo.getCode());
        limitInfoVo.setLevel(coreLimitVo.getLevel());
        limitInfoVo.setLevelNo(coreLimitVo.getLevelNo());
        limitInfoVo.setFather(coreLimitVo.getFather());
        limitInfoVo.setTopFather(coreLimitVo.getTopFather());
        limitInfoVo.setMax(new BigDecimal(coreLimitVo.getMax()));
        limitInfoVo.setActual(new BigDecimal(coreLimitVo.getActual()));
        limitInfoVo.setUseBal(new BigDecimal(coreLimitVo.getUseBal()));
        limitInfoVo.setCumBal(new BigDecimal(coreLimitVo.getCumBal()));
        limitInfoVo.setAvaBal(new BigDecimal(coreLimitVo.getAvaBal()));
        limitInfoVo.setNetBal(new BigDecimal(coreLimitVo.getNetAva()));
        limitInfoVo.setEmkBal(new BigDecimal(coreLimitVo.getEmkBal()));
        limitInfoVo.setStatus(coreLimitVo.getStatus());
        limitInfoVo.setUse(coreLimitVo.getUse());
        limitInfoVo.setCls(coreLimitVo.getCls());
        limitInfoVo.setEffDate(parseCoreDate(coreLimitVo.getEffDate()));
        limitInfoVo.setExpDate(parseCoreDate(coreLimitVo.getExpDate()));
        limitInfoVo.setNrvwDate(parseCoreDate(coreLimitVo.getNrvwDate()));
        limitInfoVo.setLrvwDate(parseCoreDate(coreLimitVo.getLrvwDate()));
        limitInfoVo.setRvwFreq(coreLimitVo.getRvwFreq());
        limitInfoVo.setExtendDate(parseCoreDate(coreLimitVo.getExtendDate()));
        limitInfoVo.setAvailDate(parseCoreDate(coreLimitVo.getAvailDate()));
        limitInfoVo.setOptionDate(parseCoreDate(coreLimitVo.getOptionDate()));
        limitInfoVo.setExtendFlg(coreLimitVo.getExtendFlg());
        limitInfoVo.setUpdateTime(ObjectUtil.isNotEmpty(byLimitNo) ? byLimitNo.getUpdateTime() : LocalDateTime.now());
        return limitInfoVo;
    }

    private LocalDateTime parseCoreDate(String coreDate) {
        if (isNullDate(coreDate)) {
            return null;
        }
        return LocalDate.parse(coreDate, formatter).atStartOfDay();
    }

    private boolean isNullDate(String coreDate) {
        if (ObjectUtil.isEmpty(coreDate)) {
            return true;
        }
        return "0".equals(coreDate);
    }



    /**
     * 商户额度冻结
     *
     * @return 冻结信息
     */
    @Transactional
    public ResultEx<LimitFreezeVo> limitFreeze(FreezeLimitDto dto) {
        //额度冻结
        MerchantLimitKo merchantLimit = limitExMapper.getMerchantLimitDetail(dto.getCustomerCode(), dto.getProdCode(), dto.getMerchantNo());
        if (ObjectUtil.isEmpty(merchantLimit)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.MERCHANT_LIMIT_EMPTY_ERROR);
        }

        LimitUserProd customerLimit = limitUserProdService.getCustomerLimit(dto.getCustomerCode(), dto.getProdCode());
        if (ObjectUtil.isEmpty(customerLimit)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.CUST_PROD_LIMIT_EMPTY_ERROR);
        }
        //---------------------
        //查询核心额度使用情况，是否足够
        //查询银行核心额度接口
        LimitInfoVo coreLimitInfoVo = getLimitInfoFromCore(customerLimit.getLimitNo());
        if (ObjectUtil.isEmpty(coreLimitInfoVo)) {
            return ResultEx.fail("查询核心额度失败");
        }
        //校验可用余额是否允许冻结
        if (dto.getUseBal().compareTo(coreLimitInfoVo.getAvaBal()) > 0) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_CU_NOT_ENOUGH);
        }
        //---------------------

        //校验可用余额是否允许冻结
        if (dto.getUseBal().compareTo(merchantLimit.getAvailBal()) > 0) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_ME_NOT_ENOUGH);
        }

        //编号生成 sequence
        //生成商户使用seqNo  MEUsequence
        String meLimitUseSeqNo = sequenceGenerator.generator(LimitUseSeqEnum.ME_LIMIT_USE_SEQ);

        //商户额度使用记录
        LimitUseRec merchantLimitUseRec = new LimitUseRec();
        merchantLimitUseRec.setSeqNo(meLimitUseSeqNo);
        merchantLimitUseRec.setLimitNo(merchantLimit.getLimitNo());
        merchantLimitUseRec.setLimitCcy(merchantLimit.getLimitCcy());
        merchantLimitUseRec.setStatus(LimitUseRecStatusEnum.FROZE.getValue());
        merchantLimitUseRec.setUseBal(dto.getUseBal());
        merchantLimitUseRec.setRecoverBal(new BigDecimal(0));
        merchantLimitUseRec.setUserType(merchantLimit.getMerchantType());
        merchantLimitUseRec.setUserNo(merchantLimit.getMerchantNo());
        merchantLimitUseRec.setUserName(merchantLimit.getMerchantName());

        limitUseRecService.save(merchantLimitUseRec);

        //调整对应limit_info记录中 可用额度与冻结额度
        int frozeMeLimitInfoCount = limitExMapper.limitInfoFroze(merchantLimit.getLimitNo(), dto.getUseBal());

        if (frozeMeLimitInfoCount != 1) {
            //额度冻结失败
            log.error("额度冻结失败：customerCode：{}, prodCode：{}, merchantNo：{}, freezeBal:{}", dto.getCustomerCode(), dto.getProdCode(), dto.getMerchantNo(), dto.getUseBal());
            log.info("额度冻结失败：frozeMeLimitInfoCount:{}", frozeMeLimitInfoCount);
            return ResultEx.fail();
        }

        LimitFreezeVo limitFreezeVo = new LimitFreezeVo();
        //返回商户额度使用流水号，除前缀其他和客户额度使用流水号一致
        limitFreezeVo.setSeqNo(meLimitUseSeqNo);
        limitFreezeVo.setCustLimitNo(customerLimit.getLimitNo());
        limitFreezeVo.setMerchantLimitNo(merchantLimit.getLimitNo());
        return ResultEx.ok(limitFreezeVo);
    }

    /**
     * 商户额度解冻
     */
    @Transactional
    public ResultEx limitUnfreeze(String seqNo) {
        //状态为已冻结 未占用
        LimitUseRec meLimitUseRec = limitUseRecService.getBySeqNo(seqNo);
        //额度使用序号不存在
        if (ObjectUtil.isEmpty(meLimitUseRec)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_ME_SEQ_NO_NOT_EXIST);
        }
        //使用流水号已被占用或已解冻
        if (!LimitUseRecStatusEnum.FROZE.getValue().equals(meLimitUseRec.getStatus())) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_SEQ_NO_FROZE_OR_USED);
        }

        //修改使用记录状态为已解冻
        int unfreezeMeLimitStatusCount = limitExMapper.updateLimitUseRecStatus(meLimitUseRec.getSeqNo(), LimitUseRecStatusEnum.FROZE.getValue(), LimitUseRecStatusEnum.UN_FROZE.getValue());

        //恢复可用额度，扣除冻结额度
        int unfreezeMeLimitInfoCount = limitExMapper.limitInfoUnFroze(meLimitUseRec.getLimitNo(), meLimitUseRec.getUseBal());

        if (unfreezeMeLimitStatusCount != 1 || unfreezeMeLimitInfoCount != 1) {
            //额度冻结失败
            log.error("额度解冻失败：seqNo:{}", seqNo);
            log.info("额度解冻失败：unfreezeMeLimitStatusCount:{},unfreezeMeLimitInfoCount:{}", unfreezeMeLimitStatusCount, unfreezeMeLimitInfoCount);
            return ResultEx.fail();
        }
        return ResultEx.ok();
    }

    /**
     * 商户额度占用
     */
    @Transactional
    public ResultEx limitUse(String seqNo) {
        //状态为已冻结
        LimitUseRec meLimitUseRec = limitUseRecService.getBySeqNo(seqNo);
        //额度使用序号不存在
        if (ObjectUtil.isEmpty(meLimitUseRec)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_ME_SEQ_NO_NOT_EXIST);
        }
        //使用流水号已被占用或已解冻
        if (!LimitUseRecStatusEnum.FROZE.getValue().equals(meLimitUseRec.getStatus())) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_SEQ_NO_FROZE_OR_USED);
        }

        //修改使用记录状态为已占用
        int updateMeLimitUseRecStatusCount = limitExMapper.updateLimitUseRecStatus(meLimitUseRec.getSeqNo(), LimitUseRecStatusEnum.FROZE.getValue(), LimitUseRecStatusEnum.USED.getValue());

        //扣除冻结额度到已用
        int useMeLimitInfoCount = limitExMapper.limitInfoUse(meLimitUseRec.getLimitNo(), meLimitUseRec.getUseBal());

        if (updateMeLimitUseRecStatusCount != 1 || useMeLimitInfoCount != 1) {
            log.error("额度占用失败：seqNo:{}", seqNo);
            log.info("额度占用失败：updateMeLimitUseRecStatusCount:{},useMeLimitInfoCount:{}", updateMeLimitUseRecStatusCount, useMeLimitInfoCount);
            return ResultEx.fail();
        }
        return ResultEx.ok();
    }

    /**
     * 冻结+占用
     */
    @Transactional
    public ResultEx<LimitFreezeUseVo> limitFreezeUse(FreezeUseLimitDto dto) {
        FreezeLimitDto freezeLimitDto = new FreezeLimitDto();
        freezeLimitDto.setProdCode(dto.getProdCode());
        freezeLimitDto.setCustomerCode(dto.getCustomerCode());
        freezeLimitDto.setMerchantNo(dto.getMerchantNo());
        freezeLimitDto.setUseBal(dto.getUseBal());

        ResultEx<LimitFreezeVo> freezeResultEx = this.limitFreeze(freezeLimitDto);
        LimitFreezeVo limitFreezeVo = freezeResultEx.getData();
        if (freezeResultEx.isFailed()) {
            return ResultEx.makeResult(freezeResultEx.getRetCode(), freezeResultEx.getRetMsg());
        }
        if (ObjectUtil.isEmpty(limitFreezeVo)) {
            return ResultEx.fail();
        }

        ResultEx limitUseResultEx = this.limitUse(limitFreezeVo.getSeqNo());
        if (limitUseResultEx.isFailed()) {
            return limitUseResultEx;
        }

        LimitFreezeUseVo freezeUseVo = new LimitFreezeUseVo();
        //返回商户额度使用流水号，除前缀其他和客户额度使用流水号一致
        freezeUseVo.setSeqNo(limitFreezeVo.getSeqNo());
        freezeUseVo.setCustLimitNo(limitFreezeVo.getCustLimitNo());
        freezeUseVo.setMerchantLimitNo(limitFreezeVo.getMerchantLimitNo());
        return ResultEx.ok(freezeUseVo);
    }

    @DS(com.mdb.bc.autoconfigure.database.MdbDataSource.SLAVE)
    public ResultEx<LimitUseRecVo> getSeqInfo(String seqNo) {
        //状态为已冻结
        LimitUseRec limitUseRec = limitUseRecService.getBySeqNo(seqNo);
        if (ObjectUtil.isEmpty(limitUseRec)) {
            return ResultEx.fail();
        }

        LimitUseRecVo limitUseRecVo = new LimitUseRecVo();
        limitUseRecVo.setId(limitUseRec.getId());
        limitUseRecVo.setSeqNo(limitUseRec.getSeqNo());
        limitUseRecVo.setLimitNo(limitUseRec.getLimitNo());
        limitUseRecVo.setLimitCcy(limitUseRec.getLimitCcy());
        limitUseRecVo.setStatus(limitUseRec.getStatus());
        limitUseRecVo.setUseBal(limitUseRec.getUseBal());
        limitUseRecVo.setRecoverBal(limitUseRec.getRecoverBal());
        limitUseRecVo.setUserType(limitUseRec.getUserType());
        limitUseRecVo.setUserNo(limitUseRec.getUserNo());
        limitUseRecVo.setUserName(limitUseRec.getUserName());
        limitUseRecVo.setCreateUserId(limitUseRec.getCreateUserId());
        limitUseRecVo.setCreateUserName(limitUseRec.getCreateUserName());
        limitUseRecVo.setCreateTime(limitUseRec.getCreateTime());
        limitUseRecVo.setUpdateTime(limitUseRec.getUpdateTime());

        return ResultEx.ok(limitUseRecVo);
    }

    /**
     * 商户额度恢复
     */
    @Transactional
    public ResultEx limitRecover(RecoverLimitDto dto) {
        //根据seqNo查询使用记录
        //状态为已占用
        LimitUseRec meLimitUseRec = limitUseRecService.getBySeqNo(dto.getSeqNo());

        //额度使用序号不存在
        if (ObjectUtil.isEmpty(meLimitUseRec)) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_ME_SEQ_NO_NOT_EXIST);
        }
        //流水号不是已占用状态
        if (!LimitUseRecStatusEnum.USED.getValue().equals(meLimitUseRec.getStatus())) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_SEQ_NO_NOT_USED_STATUS);
        }

        //根据记录中使用额度与已恢复额度 判断是否允许恢复
        if (meLimitUseRec.getUseBal().subtract(meLimitUseRec.getRecoverBal()).compareTo(dto.getRecoverBal()) < 0) {
            return ResultEx.makeResult(TradeOpenRetCodeEnum.LIMIT_ME_RECOVER_BAL_NOT_ENOUGH);//恢复金额大于商户额度使用记录可恢复金额
        }
        //是 则增加记录中已恢复额度金额  增加商户可用额度金额  增加客户可用额度金额 都不能高于总额度
        //如果调低 总额度低于已用额度时，恢复额度时要判断已用额度大于总额度时，可用额度不增加依然为0
        //恢复时规则
        int recoverMeLimitInfoCount = limitExMapper.limitInfoRecover(meLimitUseRec.getLimitNo(), dto.getRecoverBal());

        //增加使用记录中的已恢复金额
        int updateMeRecBalCount = limitExMapper.updateLimitUseRecBal(meLimitUseRec.getSeqNo(), dto.getRecoverBal());

        if (recoverMeLimitInfoCount != 1 || updateMeRecBalCount != 1) {
            log.error("额度恢复失败：seqNo:{},recoverBal:{}", dto.getSeqNo(), dto.getRecoverBal());
            log.info("额度恢复失败：recoverMeLimitInfoCount:{},updateMeRecBalCount:{}", recoverMeLimitInfoCount, updateMeRecBalCount);
            return ResultEx.fail();
        }

        LimitRecoverRec meLimitRecoverRec = new LimitRecoverRec();
        meLimitRecoverRec.setSeqNo(dto.getSeqNo());
        meLimitRecoverRec.setLimitNo(meLimitUseRec.getLimitNo());
        meLimitRecoverRec.setUseBal(meLimitUseRec.getUseBal());
        meLimitRecoverRec.setRecoverBal(dto.getRecoverBal());

        limitRecoverRecService.save(meLimitRecoverRec);
        return ResultEx.ok();
    }
}